package gn

import (
	"bytes"
	"compress/gzip"
	"crypto/tls"
	"errors"
	"io"
	"net/http"
	"net/url"
	"time"

	"gitee.com/dark.H/gs"
	"golang.org/x/net/proxy"
)

type Req struct {
	gs.NetStr
	Client    *http.Client
	Req       *http.Request
	Proxy     gs.Str
	VerifySSL bool
	UseHTTPS  bool
	Timeout   int
	RespBody  io.ReadCloser
	Err       error
	Resp      gs.NetResStr
}

func AsReq(pay gs.NetStr) (req *Req) {
	req = new(Req)
	req.NetStr = pay
	req.UseHTTPS = pay.HTTPS
	return
}

func (req *Req) HTTPS() *Req {
	req.UseHTTPS = true
	req.Build()
	return req
}

func (req *Req) SetMethod(method gs.Str) *Req {
	req.NetStr = req.NetStr.SetMethod(method)
	return req
}

func (req *Req) SetProxy(proxy gs.Str) *Req {
	req.Proxy = proxy
	err := req.Build()
	req.Err = err
	return req
}

func (req *Req) Form(key gs.Str, val any) *Req {
	req.NetStr = req.NetStr.SetForm(key, gs.S(val))
	return req
}

func (req *Req) SetCoookie(k, v gs.Str) *Req {
	req.NetStr = req.NetStr.SetCookie(k, v)
	return req
}

func (req *Req) Build() (err error) {
	trans := &http.Transport{}
	if !req.VerifySSL {
		trans.TLSClientConfig = &tls.Config{
			InsecureSkipVerify: true,
		}
	}

	if req.Proxy.StartsWith("http") {
		uri, err := url.Parse(string(req.Proxy))
		if err != nil {
			return err
		}
		trans.Proxy = http.ProxyURL(uri)
	} else if req.Proxy.StartsWith("socks5") {
		dialer, err := proxy.SOCKS5("tcp", req.Proxy.Split("://", 2).Last().Str(), nil, proxy.Direct)
		if err != nil {
			return err
		}
		trans.Dial = dialer.Dial
	}
	req.Client = &http.Client{
		Transport: trans,
	}
	if req.Timeout > 0 {
		req.Client.Timeout = time.Duration(req.Timeout) * time.Second
	}
	target := req.URL().Str()
	if req.UseHTTPS {
		target = "https://" + target
	} else {
		target = "http://" + target
	}

	req.Req, err = http.NewRequest(req.Method().Str(), target, req.BodyReader())
	if err != nil {
		return err
	}
	req.Header().Every(func(k string, v gs.Str) {
		req.Req.Header.Add(k, v.Str())
	})
	return

}

func (req *Req) Go() (respStr gs.NetResStr) {
	if req.Client == nil {
		if err := req.Build(); err != nil {
			return gs.NetResStr{Err: err}
		}
	}
	if req.Client != nil && req.Req != nil {
		if resp, err := req.Client.Do(req.Req); err != nil {
			req.Err = err
			req.Resp = respStr.AsNetRes(req.UseHTTPS, err)
			return req.Resp
		} else {
			defer resp.Body.Close()
			buffer := bytes.NewBuffer([]byte{})
			bodyBuffer := bytes.NewBuffer([]byte{})
			// resp.Write(buffer)
			// gs.Str(buffer.Bytes()).AsNetRes(req.UseHTTPS)
			// buffer.Write([]byte("\r\n"))
			if resp.Header.Get("Content-Encoding") == "gzip" {
				gw, err := gzip.NewReader(resp.Body)
				if err != nil {
					// panic(err)
					return gs.NetResStr{Err: err}
				}
				io.Copy(bodyBuffer, gw)
				gw.Close()

			} else {
				io.Copy(bodyBuffer, resp.Body)
			}
			// fmt.Println(resp.Header.Get("Content-Encoding"))
			resp.Write(buffer)
			// gs.Str(buffer.Bytes()).Color("g").Println()
			respStr = (gs.Str(bytes.SplitN(buffer.Bytes(), []byte("\r\n\r\n"), 2)[0]).Add("\r\n\r\n") + gs.Str(bodyBuffer.Bytes())).AsNetRes(req.UseHTTPS)
			req.Resp = respStr
			return
		}
	} else {
		req.Resp = respStr.AsNetRes(req.UseHTTPS, errors.New("must req.build first!!! "))
		return req.Resp
	}
}

func (req *Req) Down(filepath gs.Str) (length int64, err error) {

	nowsize, fp, err := filepath.OpenFile()
	req.SetHead("Range", gs.Str("bytes=%d-").F(nowsize))
	err = req.Build()
	if err != nil {
		return -1, err
	}
	if req.Client != nil && req.Req != nil {
		if resp, err := req.Client.Do(req.Req); err != nil {
			return -1, err
		} else {
			if err != nil {
				return -2, err
			}
			defer fp.Close()
			n, err := io.Copy(fp, resp.Body)
			if err != nil && err != io.EOF {
				return -4, err
			}

			return n, err
		}
	} else {
		return -1, errors.New("must req.build first!!! ")
	}
}

func (req *Req) GoAsReader() (http io.Reader, err error) {

	// nowsize, fp, err := filepath.OpenFile()
	// req.SetHead("Range", gs.Str("bytes=%d-").F(nowsize))
	err = req.Build()
	if err != nil {
		return nil, err
	}
	if req.Client != nil && req.Req != nil {
		if resp, err := req.Client.Do(req.Req); err != nil {
			return nil, err
		} else {
			return resp.Body, nil
		}
	} else {
		return nil, errors.New("must req.build first!!! ")
	}
}
